home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 023 / ver30 / search.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  14KB  |  673 lines

  1. /*
  2.  * Name:    MicroEMACS
  3.  *         Search commands.
  4.  * Version:    30
  5.  * Last edit:    14-Feb-86
  6.  * By:        rex::conroy, rex::ellison
  7.  *        decvax!decwrl!dec-rhea!dec-rex!conroy
  8.  *                           ...!dec-vox!ellison
  9.  *
  10.  * The functions in this file implement the
  11.  * search commands (both plain and incremental searches
  12.  * are supported) and the query-replace command.
  13.  *
  14.  * The plain old search code is part of the original
  15.  * MicroEMACS "distribution". The incremental search code,
  16.  * and the query-replace code, is by Rich Ellison.
  17.  */
  18. #include    "def.h"
  19.  
  20. #define CCHR(x)        ((x)-'@')
  21.  
  22. #define SRCH_BEGIN    (0)            /* Search sub-codes.    */
  23. #define    SRCH_FORW    (-1)
  24. #define SRCH_BACK    (-2)
  25. #define SRCH_PREV    (-3)
  26. #define SRCH_NEXT    (-4)
  27. #define SRCH_NOPR    (-5)
  28. #define SRCH_ACCM    (-6)
  29.  
  30. typedef struct  {
  31.     int    s_code;
  32.     LINE    *s_dotp;
  33.     int    s_doto;
  34. }    SRCHCOM;
  35.  
  36. static    SRCHCOM    cmds[NSRCH];
  37. static    int    cip;
  38.  
  39. int    srch_lastdir = SRCH_NOPR;        /* Last search flags.    */
  40.  
  41. /*
  42.  * Search forward.
  43.  * Get a search string from the user, and search for it,
  44.  * starting at ".". If found, "." gets moved to just after the
  45.  * matched characters, and display does all the hard stuff.
  46.  * If not found, it just prints a message.
  47.  */
  48. forwsearch(f, n, k)
  49. {
  50.     register int    s;
  51.  
  52.     if ((s=readpattern("Search")) != TRUE)
  53.         return (s);
  54.     if (forwsrch() == FALSE) {
  55.         eprintf("Not found");
  56.         return (FALSE);
  57.     }
  58.     srch_lastdir = SRCH_FORW;
  59.     return (TRUE);
  60. }
  61.  
  62. /*
  63.  * Reverse search.
  64.  * Get a search string from the  user, and search, starting at "."
  65.  * and proceeding toward the front of the buffer. If found "." is left
  66.  * pointing at the first character of the pattern [the last character that
  67.  * was matched].
  68.  */
  69. backsearch(f, n, k)
  70. {
  71.     register int    s;
  72.  
  73.     if ((s=readpattern("Reverse search")) != TRUE)
  74.         return (s);
  75.     if (backsrch() == FALSE) {
  76.         eprintf("Not found");
  77.         return (FALSE);
  78.     }
  79.     srch_lastdir = SRCH_BACK;
  80.     return (TRUE);
  81. }
  82.  
  83. /* 
  84.  * Search again, using the same search string
  85.  * and direction as the last search command. The direction
  86.  * has been saved in "srch_lastdir", so you know which way
  87.  * to go.
  88.  */
  89. searchagain(f, n, k)
  90. {
  91.     if (srch_lastdir == SRCH_FORW) {
  92.         if (forwsrch() == FALSE) {
  93.             eprintf("Not found");
  94.             return (FALSE);
  95.         }
  96.         return (TRUE);
  97.     }
  98.     if (srch_lastdir == SRCH_BACK) {
  99.         if (backsrch() == FALSE) {
  100.             eprintf("Not found");
  101.             return (FALSE);
  102.         }
  103.         return (TRUE);
  104.     }
  105.     eprintf("No last search");
  106.     return (FALSE);
  107. }
  108.  
  109. /*
  110.  * Use incremental searching, initially in the forward direction.
  111.  * isearch ignores any explicit arguments.
  112.  */
  113. forwisearch(f, n, k)
  114. {
  115.     return (isearch(SRCH_FORW));
  116. }
  117.  
  118. /*
  119.  * Use incremental searching, initially in the reverse direction.
  120.  * isearch ignores any explicit arguments.
  121.  */
  122. backisearch(f, n, k)
  123. {
  124.     return (isearch(SRCH_BACK));
  125. }
  126.  
  127. /*
  128.  * Incremental Search.
  129.  *    dir is used as the initial direction to search.
  130.  *    ^N    find next occurance  (if first thing typed reuse old string).
  131.  *    ^P    find prev occurance  (if first thing typed reuse old string).
  132.  *    ^S    switch direction to forward, find next
  133.  *    ^R    switch direction to reverse, find prev
  134.  *    ^Q    quote next character (allows searching for ^N etc.)
  135.  *    <ESC>    exit from Isearch.
  136.  *    <DEL>    undoes last character typed. (tricky job to do this correctly).
  137.  *    else    accumulate into search string
  138.  */
  139. isearch(dir)
  140. {
  141.     register int    c;
  142.     register LINE    *clp;
  143.     register int    cbo;
  144.     register int    success;
  145.     int        pptr;
  146.  
  147.     for (cip=0; cip<NSRCH; cip++)
  148.         cmds[cip].s_code = SRCH_NOPR;
  149.     cip = 0;
  150.     pptr = -1;
  151.     clp = curwp->w_dotp;
  152.     cbo = curwp->w_doto;
  153.     is_lpush();
  154.     is_cpush(SRCH_BEGIN);
  155.     success = TRUE;
  156.     is_prompt(dir, TRUE, success);
  157.     for (;;) {
  158.         update();
  159.         switch (c = ttgetc()) {
  160.         case CCHR('M'):
  161.         case METACH:
  162.             srch_lastdir = dir;
  163.             eprintf("[Done]");
  164.             return (TRUE);
  165.  
  166.         case CCHR('G'):
  167.             curwp->w_dotp = clp;
  168.             curwp->w_doto = cbo;
  169.             curwp->w_flag |= WFMOVE;
  170.             srch_lastdir = dir;
  171.             ctrlg(FALSE, 0, KRANDOM);
  172.             return (FALSE);
  173.  
  174.         case CCHR('S'):
  175.         case CCHR('F'):
  176.             if (dir == SRCH_BACK) {
  177.                 dir = SRCH_FORW;
  178.                 is_lpush();
  179.                 is_cpush(SRCH_FORW);
  180.                 success = TRUE;
  181.             }
  182.             /* Drop through to find next. */
  183.         case CCHR('N'):
  184.             if (success==FALSE && dir==SRCH_FORW)
  185.                 break;
  186.             is_lpush();
  187.             forwchar(FALSE, 1, KRANDOM);
  188.             if (is_find(SRCH_NEXT) != FALSE) {
  189.                 is_cpush(SRCH_NEXT);
  190.                 pptr = strlen(pat);
  191.             } else {
  192.                 backchar(FALSE, 1, KRANDOM);
  193.                 ttbeep();
  194.                 success = FALSE;
  195.             }
  196.             is_prompt(dir, FALSE, success);
  197.             break;
  198.  
  199.         case CCHR('R'):
  200.         case CCHR('B'):
  201.             if (dir == SRCH_FORW) {
  202.                 dir = SRCH_BACK;
  203.                 is_lpush();
  204.                 is_cpush(SRCH_BACK);
  205.                 success = TRUE;
  206.             }
  207.             /* Drop through to find previous. */
  208.         case CCHR('P'):
  209.             if (success==FALSE && dir==SRCH_BACK)
  210.                 break;
  211.             is_lpush();
  212.             backchar(FALSE, 1, KRANDOM);
  213.             if (is_find(SRCH_PREV) != FALSE) {
  214.                 is_cpush(SRCH_PREV);
  215.                 pptr = strlen(pat);
  216.             } else {
  217.                 forwchar(FALSE, 1, KRANDOM);
  218.                 ttbeep();
  219.                 success = FALSE;
  220.             }
  221.             is_prompt(dir,FALSE,success);
  222.             break;
  223.  
  224.         case 0x7F:
  225.             if (is_undo(&pptr, &dir) != TRUE)
  226.                 return (ABORT);
  227.             if (is_peek() != SRCH_ACCM)
  228.                 success = TRUE;
  229.             is_prompt(dir, FALSE, success);
  230.             break;
  231.  
  232.         case CCHR('^'):
  233.         case CCHR('Q'):
  234.             c = ttgetc();
  235.         case CCHR('U'):
  236.         case CCHR('X'):
  237.         case CCHR('J'):
  238.             goto  addchar;
  239.  
  240.         default:
  241.             if (ISCTRL(c) != FALSE) {
  242.                 c += '@';
  243.                 c |= KCTRL;
  244.                 success = execute(c, FALSE, 1);
  245.                 curwp->w_flag |= WFMOVE;
  246.                 return (success);
  247.             }                
  248.         addchar:
  249.             if (pptr == -1)
  250.                 pptr = 0;
  251.             if (pptr == 0)
  252.                 success = TRUE;
  253.             pat[pptr++] = c;
  254.             if (pptr == NPAT) {
  255.                 eprintf("Pattern too long");
  256.                 ctrlg(FALSE, 0, KRANDOM);
  257.                 return (ABORT);
  258.             }
  259.             pat[pptr] = '\0';
  260.             is_lpush();
  261.             if (success != FALSE) {
  262.                 if (is_find(dir) != FALSE)
  263.                     is_cpush(c);
  264.                 else {
  265.                     success = FALSE;
  266.                     ttbeep();
  267.                     is_cpush(SRCH_ACCM);
  268.                 }
  269.             } else
  270.                 is_cpush(SRCH_ACCM);
  271.             is_prompt(dir, FALSE, success);
  272.         }
  273.     }
  274. }
  275.  
  276. is_cpush(cmd)
  277. register int    cmd;
  278. {
  279.     if (++cip >= NSRCH)
  280.         cip = 0;
  281.     cmds[cip].s_code = cmd;
  282. }
  283.  
  284. is_lpush()
  285. {
  286.     register int    ctp;
  287.  
  288.     ctp = cip+1;
  289.     if (ctp >= NSRCH)
  290.         ctp = 0;
  291.     cmds[ctp].s_code = SRCH_NOPR;
  292.     cmds[ctp].s_doto = curwp->w_doto;
  293.     cmds[ctp].s_dotp = curwp->w_dotp;
  294. }
  295.  
  296. is_pop()
  297. {
  298.     if (cmds[cip].s_code != SRCH_NOPR) {
  299.         curwp->w_doto  = cmds[cip].s_doto; 
  300.         curwp->w_dotp  = cmds[cip].s_dotp;
  301.         curwp->w_flag |= WFMOVE;
  302.         cmds[cip].s_code = SRCH_NOPR;
  303.     }
  304.     if (--cip <= 0)
  305.         cip = NSRCH-1;
  306. }
  307.  
  308. is_peek()    
  309. {
  310.     if (cip == 0)
  311.         return (cmds[NSRCH-1].s_code);
  312.     else
  313.         return (cmds[cip-1].s_code);
  314. }
  315.  
  316. is_undo(pptr, dir)
  317. register int    *pptr;
  318. register int    *dir;
  319. {
  320.     switch (cmds[cip].s_code) {
  321.     case SRCH_NOPR:
  322.     case SRCH_BEGIN:
  323.     case SRCH_NEXT:
  324.     case SRCH_PREV:
  325.         break;
  326.  
  327.     case SRCH_FORW:
  328.         *dir = SRCH_BACK;
  329.         break;
  330.  
  331.     case SRCH_BACK:
  332.         *dir = SRCH_FORW;
  333.         break;
  334.  
  335.     case SRCH_ACCM:
  336.     default:
  337.         *pptr -= 1;
  338.         if (*pptr < 0)
  339.             *pptr = 0;
  340.         pat[*pptr] = '\0';
  341.         break;
  342.     }
  343.     is_pop();
  344.     return (TRUE);
  345. }
  346.  
  347. is_find(dir)
  348. register int    dir;
  349. {
  350.     register int    plen;
  351.  
  352.     plen = strlen(pat);
  353.     if (plen != 0) {
  354.         if (dir==SRCH_FORW || dir==SRCH_NEXT) {
  355.             backchar(FALSE, plen, KRANDOM);
  356.             if (forwsrch() == FALSE) {
  357.                 forwchar(FALSE, plen, KRANDOM);
  358.                 return (FALSE);
  359.             }
  360.             return (TRUE);
  361.         }
  362.         if (dir==SRCH_BACK || dir==SRCH_PREV) {
  363.             forwchar(FALSE, plen, KRANDOM);
  364.             if (backsrch() == FALSE) {
  365.                 backchar(FALSE, plen, KRANDOM);
  366.                 return (FALSE);
  367.             }
  368.             return (TRUE);
  369.         }
  370.         eprintf("bad call to is_find");
  371.         ctrlg(FALSE, 0, KRANDOM);
  372.         return (FALSE);
  373.     }
  374.     return (FALSE);
  375. }
  376.  
  377. /*
  378.  * If called with "dir" not one of SRCH_FORW
  379.  * or SRCH_BACK, this routine used to print an error
  380.  * message. It also used to return TRUE or FALSE,
  381.  * depending on if it liked the "dir". However, none
  382.  * of the callers looked at the status, so I just
  383.  * made the checking vanish.
  384.  */
  385. is_prompt(dir, flag, success)
  386. {
  387.     if (dir == SRCH_FORW) {
  388.         if (success != FALSE)
  389.             is_dspl("i-search forward", flag);
  390.         else
  391.             is_dspl("failing i-search forward", flag);
  392.     } else if (dir == SRCH_BACK) {
  393.         if (success != FALSE)
  394.             is_dspl("i-search backward", flag);
  395.         else
  396.             is_dspl("failing i-search backward", flag);
  397.     }
  398. }
  399.  
  400. /*
  401.  * Prompt writing routine for the incremental search. 
  402.  * The "prompt" is just a string. The "flag" determines
  403.  * if a "[ ]" or ":" embelishment is used.
  404.  */
  405. is_dspl(prompt, flag)
  406. char    *prompt;
  407. {
  408.     if (flag != FALSE)
  409.         eprintf("%s [%s]", prompt, pat);
  410.     else
  411.         eprintf("%s: %s", prompt, pat);
  412. }
  413.  
  414. /*
  415.  * Query Replace.
  416.  *    Replace strings selectively.  Does a search and replace operation.
  417.  *    A space or a comma replaces the string, a period replaces and quits,
  418.  *    an n doesn't replace, a C-G quits.
  419.  */
  420. queryrepl(f, n, k)
  421. {
  422.     register int    s;
  423.     char        news[NPAT];    /* replacement string        */
  424.     register int    kludge;        /* Watch for saved line move    */
  425.     LINE        *clp;        /* saved line pointer        */
  426.     int        cbo;        /* offset into the saved line    */
  427.     int        rcnt = 0;    /* Replacements made so far    */
  428.     int        plen;        /* length of found string    */
  429.  
  430.     if ((s=readpattern("Old string")) != TRUE)
  431.         return (s);
  432.     if ((s=ereply("New string: ",news, NPAT)) == ABORT)
  433.         return (s);
  434.     if (s == FALSE)
  435.         news[0] = '\0';
  436.     eprintf("Query Replace:  [%s] -> [%s]", pat, news);
  437.     plen = strlen(pat);
  438.  
  439.     /*
  440.      * Search forward repeatedly, checking each time whether to insert
  441.      * or not.  The "!" case makes the check always true, so it gets put
  442.      * into a tighter loop for efficiency.
  443.      *
  444.      * If we change the line that is the remembered value of dot, then
  445.      * it is possible for the remembered value to move.  This causes great
  446.      * pain when trying to return to the non-existant line.
  447.      *
  448.      * possible fixes:
  449.      * 1) put a single, relocated marker in the WINDOW structure, handled
  450.      *    like mark.  The problem now becomes a what if two are needed...
  451.      * 2) link markers into a list that gets updated (auto structures for
  452.      *    the nodes)
  453.      * 3) Expand the mark into a stack of marks and add pushmark, popmark.
  454.      */
  455.  
  456.     clp = curwp->w_dotp;        /* save the return location    */
  457.     cbo = curwp->w_doto;
  458.     while (forwsrch() == TRUE) {
  459.     retry:
  460.         update();
  461.         switch (ttgetc()) {
  462.         case ' ':
  463.         case ',':
  464.             kludge = (curwp->w_dotp == clp);
  465.             if (lreplace(plen, news, f) == FALSE)
  466.                 return (FALSE);
  467.             rcnt++;
  468.             if (kludge != FALSE)
  469.                 clp = curwp->w_dotp;
  470.             break;
  471.  
  472.         case '.':
  473.             kludge = (curwp->w_dotp == clp);
  474.             if (lreplace(plen, news, f) == FALSE)
  475.                 return (FALSE);
  476.             rcnt++;
  477.             if (kludge != FALSE)
  478.                 clp = curwp->w_dotp;
  479.             goto stopsearch;
  480.  
  481.         case CCHR('G'):
  482.             ctrlg(FALSE, 0, KRANDOM);
  483.             goto stopsearch;
  484.  
  485.         case '!':
  486.             do {
  487.                 kludge = (curwp->w_dotp == clp);
  488.                 if (lreplace(plen, news, f) == FALSE)
  489.                     return (FALSE);
  490.                 rcnt++;
  491.                 if (kludge != FALSE)
  492.                     clp = curwp->w_dotp;
  493.             } while (forwsrch() == TRUE);
  494.             goto stopsearch;
  495.  
  496.         case 'n':
  497.             break;
  498.  
  499.         default:
  500. eprintf("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
  501.             goto retry;
  502.         }
  503.     }
  504. stopsearch:
  505.     curwp->w_dotp = clp;
  506.     curwp->w_doto = cbo;
  507.     curwp->w_flag |= WFHARD;
  508.     update();
  509.     if (rcnt == 0)
  510.         eprintf("[No replacements done]");
  511.     else if (rcnt == 1)
  512.         eprintf("[1 replacement done]");
  513.     else
  514.         eprintf("[%d replacements done]", rcnt);
  515.     return (TRUE);
  516. }
  517.  
  518. /*
  519.  * This routine does the real work of a
  520.  * forward search. The pattern is sitting in the external
  521.  * variable "pat". If found, dot is updated, the window system
  522.  * is notified of the change, and TRUE is returned. If the
  523.  * string isn't found, FALSE is returned.
  524.  */
  525. forwsrch()
  526. {
  527.     register LINE    *clp;
  528.     register int    cbo;
  529.     register LINE    *tlp;
  530.     register int    tbo;
  531.     register char    *pp;
  532.     register int    c;
  533.  
  534.     clp = curwp->w_dotp;
  535.     cbo = curwp->w_doto;
  536.     while (clp != curbp->b_linep) {
  537.         if (cbo == llength(clp)) {
  538.             clp = lforw(clp);
  539.             cbo = 0;
  540.             c = '\n';
  541.         } else
  542.             c = lgetc(clp, cbo++);
  543.         if (eq(c, pat[0]) != FALSE) {
  544.             tlp = clp;
  545.             tbo = cbo;
  546.             pp  = &pat[1];
  547.             while (*pp != 0) {
  548.                 if (tlp == curbp->b_linep)
  549.                     goto fail;
  550.                 if (tbo == llength(tlp)) {
  551.                     tlp = lforw(tlp);
  552.                     if (tlp == curbp->b_linep)
  553.                         goto fail;
  554.                     tbo = 0;
  555.                     c = '\n';
  556.                 } else
  557.                     c = lgetc(tlp, tbo++);
  558.                 if (eq(c, *pp++) == FALSE)
  559.                     goto fail;
  560.             }
  561.             curwp->w_dotp  = tlp;
  562.             curwp->w_doto  = tbo;
  563.             curwp->w_flag |= WFMOVE;
  564.             return (TRUE);
  565.         }
  566.     fail:    ;
  567.     }
  568.     return (FALSE);
  569. }
  570.  
  571. /*
  572.  * This routine does the real work of a
  573.  * backward search. The pattern is sitting in the external
  574.  * variable "pat". If found, dot is updated, the window system
  575.  * is notified of the change, and TRUE is returned. If the
  576.  * string isn't found, FALSE is returned.
  577.  */
  578. backsrch()
  579. {
  580.     register LINE    *clp;
  581.     register int    cbo;
  582.     register LINE    *tlp;
  583.     register int    tbo;
  584.     register int    c;
  585.     register char    *epp;
  586.     register char    *pp;
  587.  
  588.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  589.         ;
  590.     clp = curwp->w_dotp;
  591.     cbo = curwp->w_doto;
  592.     for (;;) {
  593.         if (cbo == 0) {
  594.             clp = lback(clp);
  595.             if (clp == curbp->b_linep)
  596.                 return (FALSE);
  597.             cbo = llength(clp)+1;
  598.         }
  599.         if (--cbo == llength(clp))
  600.             c = '\n';
  601.         else
  602.             c = lgetc(clp,cbo);
  603.         if (eq(c, *epp) != FALSE) {
  604.             tlp = clp;
  605.             tbo = cbo;
  606.             pp  = epp;
  607.             while (pp != &pat[0]) {
  608.                 if (tbo == 0) {
  609.                     tlp = lback(tlp);
  610.                     if (tlp == curbp->b_linep)
  611.                         goto fail;
  612.                     tbo = llength(tlp)+1;
  613.                 }
  614.                 if (--tbo == llength(tlp))
  615.                     c = '\n';
  616.                 else
  617.                     c = lgetc(tlp,tbo);
  618.                 if (eq(c, *--pp) == FALSE)
  619.                     goto fail;
  620.             }
  621.             curwp->w_dotp  = tlp;
  622.             curwp->w_doto  = tbo;
  623.             curwp->w_flag |= WFMOVE;
  624.             return (TRUE);
  625.         }
  626.     fail:    ;
  627.     }
  628. }
  629.  
  630. /*
  631.  * Compare two characters.
  632.  * The "bc" comes from the buffer.
  633.  * It has its case folded out. The
  634.  * "pc" is from the pattern.
  635.  */
  636. eq(bc, pc)
  637. {
  638.     register int    ibc;
  639.     register int    ipc;
  640.  
  641.     ibc = bc & 0xFF;
  642.     ipc = pc & 0xFF;
  643.     if (ISLOWER(ibc) != FALSE)
  644.         ibc = TOUPPER(ibc);
  645.     if (ISLOWER(ipc) != FALSE)
  646.         ipc = TOUPPER(ipc);
  647.     if (ibc == ipc)
  648.         return (TRUE);
  649.     return (FALSE);
  650. }
  651.  
  652. /*
  653.  * Read a pattern.
  654.  * Stash it in the external variable "pat". The "pat" is
  655.  * not updated if the user types in an empty line. If the user typed
  656.  * an empty line, and there is no old pattern, it is an error.
  657.  * Display the old pattern, in the style of Jeff Lomicka. There is
  658.  * some do-it-yourself control expansion.
  659.  */
  660. readpattern(prompt)
  661. char    *prompt;
  662. {
  663.     register int    s;
  664.     char        tpat[NPAT];
  665.  
  666.     s = ereply("%s [%s]: ", tpat, NPAT, prompt, pat);
  667.     if (s == TRUE)                /* Specified        */
  668.         strcpy(pat, tpat);
  669.     else if (s==FALSE && pat[0]!=0)        /* CR, but old one    */
  670.         s = TRUE;
  671.     return (s);
  672. }
  673.